﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using DevConHelper;

public static class DevCon {
	static MainForm _form;
	readonly static object mfSingletoneSync = new object();
	static MainForm form {
		get {
			lock (mfSingletoneSync) {
				if (_form == null) {
					_form = new MainForm();
				}
			}
			if (!_form.Visible) _form.Visible = true;
			return _form;
		}
	}
	public static void Log(string msg, params object[] args) {
		string thread = Thread.CurrentThread.ManagedThreadId + Thread.CurrentThread.Name;
		msg = DateTime.Now.ToString("[dd.HH:mm:ss.f") + " T" + thread + "] " + FormatIfNeed(msg, args) + "\r\n";
		if (form.InvokeRequired) {
			form.Invoke((Action<string>)formAppend, msg);
		} else {
			formAppend(msg);
		}
	}

	static void formAppend(string message) {
		form.rtb.AppendText(message);
	}

	public static void DevLog(this object obj) {
		Log(Inspect(obj));
	}
	public static void DevLog(this object obj, string msg, params object[] args) {
		Log(FormatIfNeed(msg, args) + ": " + Inspect(obj));
	}

	static string FormatIfNeed(string msg, params object[] args) {
		if (args.Length == 0) return msg;
		else return string.Format(msg, args);
	}

	//public static void Ensure(bool exp) {
	//   Ensure(exp, null);
	//}
	//public static void EnsureExists(this object obj) {
	//    Ensure(obj != null, "Object is not exists");
	//}
	//public static void Ensure(this bool exp) {
	//    Ensure(exp, "Assertion Failed");
	//}
	//public static void Ensure(this bool exp, string msg) {
	//    if (!exp) throw new ApplicationException("Contract failure exception" + (string.IsNullOrEmpty(msg) ? "" : ": " + msg));
	//}
	//public static void EnsureFalse(this bool exp) {
	//    Ensure(!exp, "Assertion Failed");
	//}
	//public static void Failed() {
	//    Failed("User defined");
	//}
	//public static void Failed(string msg) {
	//    Ensure(false, "Failed: "+msg);
	//}
	public static void Each<T>(this Action<T> d, IEnumerable col) {
		foreach (T item in col)
			d.Invoke(item);
	}
	public static string Inspect(this object obj) {
		if (obj == null) return "(null)";
		string s = obj.ToString();
		if (s == null) s = "(null was returned by ToString, but object != null)";

		string suf = "";
		string postfix = "";
		if (s.Length > 255) {
			var fn = Path.GetTempFileName() + ".txt";
			File.WriteAllText(fn, s);
			fn = fn.Replace(":\\", "/").Replace("\\", "/");
			suf = "LargeData";
			s = "exported to file://" + fn;
		}
		else {
			var pre = s;
			s = s.Replace("\0", "\\0");
			s = s.Replace("\t", "\\t");
			s = s.Replace("\n", "\\n");
			s = s.Replace("\r", "\\r");
			if (s != pre) suf = "Escaped";
			if (s.Length == 0) s = "\"\"";
		}
		if (suf.Length > 0) suf = "(" + suf + ")";
		var type = obj.GetType();
		if (type.IsEnum) {
			//type.UnderlyingSystemType
			Type ut = Enum.GetUnderlyingType(type);
			if (ut == typeof(int)) postfix = ((int)obj).ToString();
			else if (ut == typeof(byte)) postfix = ((byte)obj).ToString();
			//		else {
			//				postfix = "@"+ut.Name;
			//		}
			postfix += "@" + ut.Name;
		}
		if (postfix.Length > 0) postfix = " " + suf;
		return string.Format("{0}{1}:{2}{3}", type.Name, suf, s, postfix);
	}
	public static string InspectRecur(this object obj) {
		var sb = new StringBuilder();
		var dicVisited = new Dictionary<object, object>(256);
		sb.Append(obj.Inspect()).AppendLine();
		InspectRecur(obj, ref sb, 1,ref dicVisited);
		return sb.ToString();
	}

	static void InspectRecur(object obj, ref StringBuilder sb, int tab, ref Dictionary<object,object> visited) {
		if (obj == null) return;
		if (visited.ContainsKey(obj)) {
			sb.Append('\t', tab - 1).AppendLine("(Visited)");
			return;
		}
		visited.Add(obj, null);
		foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) {
			object val = fi.GetValue(obj);
			sb.Append('\t', tab).Append(fi.Name).Append(" = ").Append(val.Inspect()).AppendLine();
			if (fi.FieldType.IsPrimitive) continue;
			if (val is string) continue;
			if (val is Enum) continue;
			InspectRecur(val, ref  sb, tab + 1, ref visited);
		}
	}
}

